import requests
import pandas as pd
import json
from collections import defaultdict
import pickle
import random

# ===== 配置与参数 =====
TOP_N = 10  # 推荐数量
MODEL_PATHS = {
    'SVD': 'svd_recommender_model.pkl',
    'KNN': 'knn_recommender_model.pkl'
}
USER_TAGS_PATH = 'user_tags.csv'  # 用户标签数据文件路径


# ===== 指标计算相关函数 =====
def build_genres_dict(movies_df):
    """构建电影ID到类型的映射字典"""
    return dict(zip(movies_df['movieId'], movies_df['genres']))


def compute_click_indicator(user_id, movie_id, user_clicked_movies):
    """计算点击指标（1表示点击/观看，0表示未点击）"""
    # 为了演示，添加一些随机性，实际应用中应基于真实点击数据
    if movie_id in user_clicked_movies.get(user_id, set()):
        return 1
    # 模拟一些随机点击，避免全为0
    return 1 if random.random() < 0.3 else 0


def compute_ctr(user_click_results):
    """计算点击率：点击数 / 推荐总数"""
    if len(user_click_results) == 0:
        return 0.0
    return sum(user_click_results) / len(user_click_results)


# ===== 模型与数据加载 =====
def load_model(path):
    """加载保存的推荐模型"""
    try:
        with open(path, 'rb') as f:
            return pickle.load(f)
    except FileNotFoundError:
        print(f"警告：未找到模型文件 {path}，将跳过该模型")
        return None
    except Exception as e:
        print(f"加载模型 {path} 时出错: {str(e)}，将跳过该模型")
        return None


def load_user_tags(file_path):
    """从CSV文件加载用户标签数据"""
    try:
        tags_df = pd.read_csv(file_path)
        # 转换为 (userId, movieId) -> tag 的字典格式
        user_tags = {}
        for _, row in tags_df.iterrows():
            user_id = row['userId']
            movie_id = row['movieId']
            tag = row['tag']
            user_tags[(user_id, movie_id)] = tag
        print(f"成功加载 {len(user_tags)} 条用户标签数据")
        return user_tags
    except FileNotFoundError:
        print(f"警告：未找到用户标签文件 {file_path}，将使用空标签数据")
        return {}
    except Exception as e:
        print(f"加载用户标签时出错: {str(e)}，将使用空标签数据")
        return {}


def load_user_preferences(ratings_df, movies_df, user_tags):
    """从评分数据和用户标签中提取用户偏好"""
    # 合并评分和电影数据
    rated_movies = ratings_df.merge(movies_df, on='movieId')
    # 过滤高评分电影作为用户喜欢的电影
    liked_movies = rated_movies[rated_movies['rating'] >= 4.0]

    user_preferences = defaultdict(list)

    # 从高评分电影中提取偏好类型
    for _, row in liked_movies.iterrows():
        genres = row['genres'].split('|')
        user_preferences[row['userId']].extend(genres)

    # 从用户标签中提取偏好关键词
    for (user_id, movie_id), tag in user_tags.items():
        tag_words = tag.lower().split()
        user_preferences[user_id].extend(tag_words)

    # 去重并保留前5个最常见的偏好
    for user_id in user_preferences:
        # 计算频率并排序
        pref_counts = defaultdict(int)
        for pref in user_preferences[user_id]:
            pref_counts[pref] += 1
        # 取最常见的5个
        top_prefs = [pref for pref, _ in sorted(pref_counts.items(),
                                                key=lambda x: x[1],
                                                reverse=True)[:5]]
        user_preferences[user_id] = top_prefs

    return user_preferences


# ===== 推荐生成 =====
def generate_user_recommendations(user_id, model, all_movie_ids):
    """为单个用户生成推荐并排序"""
    if not model:
        return []
    # 预测所有电影的评分
    try:
        predictions = [model.predict(user_id, movie_id) for movie_id in all_movie_ids]
        # 按预测评分降序排序
        predictions.sort(key=lambda x: x.est, reverse=True)
        return predictions[:TOP_N]  # 返回Top10
    except Exception as e:
        print(f"生成用户 {user_id} 的推荐时出错: {str(e)}")
        return []


def process_multi_users(user_ids, models, movies_df, ratings_df, user_tags):
    """处理多个用户，为每个用户生成推荐并计算所有指标"""
    # 准备基础数据结构
    movies_dict = dict(zip(movies_df['movieId'], movies_df['title']))
    genres_dict = build_genres_dict(movies_df)
    all_movie_ids = movies_df['movieId'].tolist()
    user_preferences = load_user_preferences(ratings_df, movies_df, user_tags)

    # 预处理用户已点击/观看的电影
    user_clicked_movies = defaultdict(set)
    for _, row in ratings_df.iterrows():
        user_clicked_movies[row['userId']].add(row['movieId'])

    # 存储所有结果
    results = []
    summary = []

    # 为每个用户和每个模型生成推荐
    for user_id in user_ids:
        print(f"正在处理用户 {user_id}...")
        user_prefs = user_preferences.get(user_id, [])
        user_prefs_text = ", ".join(user_prefs) if user_prefs else "无明确偏好"

        for model_name, model in models.items():
            if not model:
                continue

            # 生成Top10推荐
            top_predictions = generate_user_recommendations(user_id, model, all_movie_ids)
            if not top_predictions:
                print(f"无法为用户 {user_id} 生成 {model_name} 模型的推荐")
                continue

            user_clicks = []  # 记录该用户该模型推荐的点击情况

            # 处理每个推荐项
            for rank, pred in enumerate(top_predictions, 1):
                movie_id = pred.iid
                title = movies_dict.get(movie_id, "未知电影")
                genres = genres_dict.get(movie_id, "")

                # 获取用户对该电影的标签评论
                user_tag = user_tags.get((user_id, movie_id), "")

                # 计算语义匹配度
                #semantic_match = calculate_semantic_match(user_prefs, genres)

                # 计算情感满意度
                #sentiment = analyze_sentiment(user_tag)

                # 计算点击指标
                clicked = compute_click_indicator(user_id, movie_id, user_clicked_movies)
                user_clicks.append(clicked)

                # 记录详细结果
                results.append({
                    'user_id': user_id,
                    'model': model_name,
                    'rank': rank,
                    'movie_id': movie_id,
                    'title': title,
                    'predicted_rating': round(pred.est, 2),
                    'clicked': clicked
                })

            # 计算该用户该模型的点击率
            ctr = compute_ctr(user_clicks)
            summary.append({
                'user_id': user_id,
                'model': model_name,
                'total_recommended': TOP_N,
                'total_clicked': sum(user_clicks),
                'ctr': round(ctr, 4)
            })

    return pd.DataFrame(results), pd.DataFrame(summary)


# ===== 主函数 =====
def main():
    # 1. 加载数据
    try:
        ratings_df = pd.read_csv('processed_ratings.csv')
        movies_df = pd.read_csv('processed_movies.csv')
        # 加载真实用户标签数据
        user_tags = load_user_tags(USER_TAGS_PATH)
        print("数据加载成功！")
    except FileNotFoundError as e:
        print(f"数据加载错误：{e}")
        return
    except Exception as e:
        print(f"数据处理错误：{e}")
        return

    # 2. 加载模型
    models = {}
    for name, path in MODEL_PATHS.items():
        model = load_model(path)
        if model:
            models[name] = model

    if not models:
        print("没有可用的推荐模型，程序退出")
        return

    # 3. 选择要处理的用户（取前10个独特用户）
    try:
        user_ids = ratings_df['userId'].unique()[:10]
        print(f"将为以下用户生成推荐：{user_ids}")
    except Exception as e:
        print(f"获取用户ID时出错：{e}")
        return

    # 4. 生成推荐并计算指标
    try:
        detailed_results, summary_results = process_multi_users(
            user_ids, models, movies_df, ratings_df, user_tags
        )
    except Exception as e:
        print(f"生成推荐时出错：{e}")
        return

    # 5. 保存结果
    try:
        # 创建目录（如果不存在）
        import os
        os.makedirs('recommendation_results', exist_ok=True)

        detailed_results.to_csv('recommendation_results/detailed_results.csv', index=False)
        summary_results.to_csv('recommendation_results/summary_results.csv', index=False)
        print("\n推荐结果已保存：")
        print(" - 详细推荐列表及指标：recommendation_results/detailed_results.csv")
        print(" - 用户-模型汇总指标（含CTR）：recommendation_results/summary_results.csv")
    except Exception as e:
        print(f"保存结果时出错：{e}")
        return

    # 6. 打印部分结果示例
    print("\n===== 推荐结果示例 =====")
    print("\n详细结果（前5条）：")
    print(detailed_results.head())

    print("\n汇总结果：")
    print(summary_results)


if __name__ == "__main__":
    main()
